using System;
using System.Collections;
using UnityEngine;

public class AiPlayer : PlayerBase
{
	public struct AIHitBallParams
	{
		public HitBallParams hitBallParams;

		public float usedSpeed;
	}

	public Transform racket;

	public FloatRange speedRange = new FloatRange
	{
		min = 0f,
		max = 1f
	};

	protected Vector3 startMovePos;

	protected Vector3 endMovePos;

	protected bool moving;

	protected float moveTime;

	protected float moveDuration;

	public float pingTime = 45f;

	public float rotateDuration = 0.3f;

	public float rotateMoveBackDuration = 0.1f;

	public float rotateAngle = 35f;

	public Vector3 racketRotateAxis = Vector3.right;

	protected Vector3 additionalMove;

	protected Action onMoveComplete;

	protected Quaternion additionalRotation = Quaternion.identity;

	protected float racketY;

	protected Vector3 racketPosition;

	protected GravityFlight serveFlight = new GravityFlight();

	protected AiPlayerDefinition ai
	{
		get
		{
			return ScriptableObjectSingleton<AiPlayerDefinitions>.instance.GetDefinition(base.match.ui.aiTestButton.playerDefinitionIndex);
		}
	}

	private float scoreDifference
	{
		get
		{
			return base.match.gameType.GetScoreDifference(base.teamTag);
		}
	}

	public override void Init(int teamTag, int tableSide, MatchController match)
	{
		base.Init(teamTag, tableSide, match);
		racket.rotation = Quaternion.AngleAxis(70f, Vector3.forward);
		racketY = Table.tabletopy + 0.2f;
		racketPosition = racket.position;
	}

	private void Update()
	{
		if (base.isInServe)
		{
			Vector3 position = racket.position + -base.tableSide * Vector3.forward * base.ball.myRadius;
			serveFlight.timeOfFlight = Mathf.Repeat(serveFlight.timeOfFlight + Time.deltaTime, 2f * serveFlight.apexTime);
			position.y = serveFlight.Position(serveFlight.timeOfFlight).y;
			position.x = Mathf.Clamp(position.x, 0f - Table.halfwidth, Table.halfwidth);
			position.z = (float)base.tableSide * Table.halfLength;
			base.ball.position = position;
		}
		Ball ball = base.match.ball;
		float num = ball.position.x - racket.position.x;
		racket.rotation = Quaternion.Lerp(racket.rotation, Quaternion.AngleAxis((float)MathEx.SignZeroPositive(num) * Mathf.Lerp(0f, 1f, 4f * Mathf.Abs(num) / Table.halfwidth) * -70f, Vector3.forward), Time.deltaTime * 20f);
		UpdateMove();
		racket.position = racketPosition + additionalMove;
	}

	private void RotateToHit()
	{
		StartCoroutine(DoRotateToHit());
	}

	private IEnumerator DoRotateToHit()
	{
		additionalRotation = Quaternion.identity;
		float time2 = 0f;
		Quaternion centerRotation = Quaternion.AngleAxis(rotateAngle * (float)(-base.tableSide), racketRotateAxis);
		while (time2 < rotateDuration)
		{
			yield return null;
			time2 += Time.deltaTime;
			additionalRotation = Quaternion.Lerp(Quaternion.identity, centerRotation, time2 / rotateDuration);
		}
		time2 = 0f;
		while (time2 < rotateMoveBackDuration)
		{
			yield return null;
			time2 += Time.deltaTime;
			additionalRotation = Quaternion.Lerp(centerRotation, Quaternion.identity, time2 / rotateMoveBackDuration);
		}
	}

	private AIHitBallParams GetHitBallParams(Vector3 ballPos)
	{
		float num = ai.shotSpeed.Random(scoreDifference);
		RacketSettings.Racket currentRacket = base.match.ui.racketButton.currentRacket;
		float num2 = currentRacket.zLandingPosRange.Lerp(num);
		float num3 = currentRacket.heightOverNet.Lerp(num);
		float spiny = currentRacket.downSpin.Lerp(num);
		float timeToLand = currentRacket.ballVelocityRange.Lerp(num);
		float x = Table.halfwidth * ai.aimRange.Random() * (float)MathEx.SignZeroPositive(ballPos.x) * Mathf.Lerp(1f, -1f, ai.aim.Random(scoreDifference));
		Vector3 vector = new Vector3(x, Table.tabletopy, (float)(-base.tableSide) * Table.halfLength * num2);
		bool useNormalTime = true;
		float spinx = 0f;
		float airResistance = 0f;
		float rebound = 1f;
		float num4 = num * currentRacket.fingerSpeedRange.max;
		if (currentRacket.type == RacketSettings.SettingsType.RangeSettings)
		{
			RacketSettings.RacketRangeSettings racketRangeSettings = currentRacket.ranges[currentRacket.ranges.Count - 1];
			num4 = num * racketRangeSettings.fingerSpeedRange.max;
			RacketSettings.RacketRangeSettings settingsForRange = currentRacket.GetSettingsForRange(num4);
			num2 = settingsForRange.zLandingPosRange.Lerp(num);
			num3 = settingsForRange.heightOverNet.Lerp(num);
			spiny = settingsForRange.downSpin.Lerp(num);
			vector = new Vector3(x, Table.tabletopy, (float)(-base.tableSide) * Table.halfLength * num2);
			timeToLand = settingsForRange.GetTimeToLand(num, vector, ballPos);
			useNormalTime = settingsForRange.useNormalTime;
			Vector3 normalized = (vector - ballPos).OnGround().normalized;
			if (Mathf.Abs(normalized.x) > settingsForRange.normalizedSpinLimit)
			{
				spinx = Mathf.Sign(normalized.x) * settingsForRange.spin;
			}
			spinx = (float)((UnityEngine.Random.Range(0, 100) >= 50) ? 1 : (-1)) * Mathf.Clamp01(ai.spinx.Random(scoreDifference)) * settingsForRange.spin;
			airResistance = settingsForRange.airResistance;
			rebound = settingsForRange.rebound;
		}
		HitBallParams hitBallParams = default(HitBallParams);
		hitBallParams.landingPosition = vector;
		hitBallParams.airResistance = airResistance;
		hitBallParams.heightAboveNet = Table.netHeight + num3;
		hitBallParams.timeToLand = timeToLand;
		hitBallParams.spinx = spinx;
		hitBallParams.spiny = spiny;
		hitBallParams.useNormalTime = useNormalTime;
		hitBallParams.rebound = rebound;
		hitBallParams.racketTimeScale = currentRacket.timeScale;
		AIHitBallParams result = default(AIHitBallParams);
		result.hitBallParams = hitBallParams;
		result.usedSpeed = num4;
		return result;
	}

	private void HitBall(HitBallParams p)
	{
		if (base.isInServe)
		{
			Vector3 position = base.ball.position;
			position.y = Mathf.Max(racket.position.y, position.y);
			base.ball.position = position;
		}
		Ball ball = base.ball;
		p.heightAboveNet *= ai.shotHeightMult;
		p.timeToLand *= ai.timeToLandMult;
		HitBall(p.landingPosition, p.heightAboveNet, p.timeToLand, p.spinx, p.spiny, base.isInServe, p.useNormalTime, p.airResistance, p.rebound, p.racketTimeScale);
		base.isInServe = false;
		float durationTillStage = ball.flightPlan.GetDurationTillStage(ball.flightPlan.reboundStage);
		float timeScale = 1f + pingTime * 0.001f / Time.timeScale / durationTillStage;
		ball.flightPlan.timeScale = timeScale;
		PrepareForReturn(ball);
	}

	private void PrepareForReturn(Ball ball)
	{
		Vector3 landingPos = ball.flightPlan.landingPos;
		if (Table.IsPosOnTable(landingPos))
		{
			float moveSpeed = GetNewMoveSpeed() * 0.5f;
			Vector3 position = racket.position;
			float t = ai.returnPosition.Random(scoreDifference);
			position.z = Mathf.Lerp(racket.position.z, (float)base.tableSide * Table.halfLength, t);
			position.x = Mathf.Lerp(racket.position.x, 0f, t);
			MoveToWithSpeed(position, moveSpeed, null);
		}
	}

	private void UpdateMove()
	{
		if (!moving)
		{
			return;
		}
		moveTime += Time.deltaTime;
		float t = 1f;
		if (moveDuration > 0f)
		{
			t = MathEx.Hermite(moveTime / moveDuration);
		}
		racketPosition = Vector3.Lerp(startMovePos, endMovePos, t);
		if (moveTime >= moveDuration)
		{
			moving = false;
			if (onMoveComplete != null)
			{
				onMoveComplete();
			}
		}
	}

	private IEnumerator DoAdditionalMove(Vector3 start, Vector3 end, float delay, float durationBack, float durationFwd, Action onComplete)
	{
		if (delay > 0f)
		{
			yield return new WaitForSeconds(delay);
		}
		additionalMove = Vector3.zero;
		float time2 = 0f;
		while (time2 < durationBack)
		{
			time2 += Time.deltaTime;
			additionalMove = Vector3.Lerp(start, end, time2 / durationBack);
			yield return null;
		}
		time2 = 0f;
		while (time2 < durationFwd)
		{
			time2 += Time.deltaTime;
			additionalMove = Vector3.Lerp(end, start, time2 / durationFwd);
			yield return null;
		}
		if (onComplete != null)
		{
			onComplete();
		}
	}

	private void MoveToWithSpeed(Vector3 pos, float moveSpeed, Action onMoveComplete)
	{
		float duration = Vector3.Distance(pos, racket.position) / moveSpeed;
		MoveTo(pos, duration, onMoveComplete);
	}

	private void MoveTo(Vector3 pos, float duration, Action onMoveComplete)
	{
		moveTime = 0f;
		moveDuration = duration;
		moving = true;
		startMovePos = racket.position;
		endMovePos = pos;
		this.onMoveComplete = onMoveComplete;
	}

	public override void StartServe(Ball ball)
	{
		base.ball = ball;
		ball.TakeBall();
		base.isInServe = true;
		Vector3 position = racket.position;
		position.y = Table.tabletopy;
		position.x = Mathf.Clamp(position.x, 0f - Table.halfwidth, Table.halfwidth);
		ball.position = position;
		serveFlight.Fire(position, ParabolicFlight.initialVelocityForSimpleCase(position, position, Table.tabletopy + 0.3f), Physics.gravity);
		Vector3 positionToMoveTo = new Vector3(0f - Table.halfwidth, racketY, (float)base.tableSide * Table.halfLength * 1.25f);
		MoveToWithSpeed(positionToMoveTo, GetNewMoveSpeed(), delegate
		{
			AIHitBallParams hitParams = GetHitBallParams(positionToMoveTo);
			StartCoroutine(DoAdditionalMove(Vector3.zero, Vector3.forward * base.tableSide * 0.3f, 0f, 0.3f, 0.1f, delegate
			{
				HitBall(hitParams.hitBallParams);
			}));
		});
	}

	public override void ReceiveServe()
	{
		base.ball = null;
		Vector3 pos = new Vector3(0f, racketY, (float)base.tableSide * Table.halfLength * 1.25f);
		float moveSpeed = 4f;
		MoveToWithSpeed(pos, moveSpeed, null);
	}

	public override void ReceiveBall(Ball ball)
	{
		base.ball = ball;
		StartCoroutine(DoReceiveBall(ball));
	}

	private float GetNewMoveSpeed()
	{
		float num = ai.moveSpeed.Random(scoreDifference);
		if (base.match.ball.isBallInGame)
		{
			float t = ai.tiredNumShotsRange.InverseLerp(base.match.gameType.numShotsInPoint);
			num *= Mathf.Lerp(1f, ai.tiredSpeedMult, t);
		}
		return num;
	}

	private IEnumerator DoReceiveBall(Ball ball)
	{
		yield return null;
		if (!Table.IsPosOnTable(ball.flightPlan.landingPos))
		{
			yield break;
		}
		BallFlightPlan.FlightPlanStage lastStage = ball.flightPlan.LastStage();
		float totalDuration = ball.flightPlan.GetTotalDuration();
		Vector3 apexPos = lastStage.flight.Position(lastStage.flight.apexTime);
		Vector3 positionToMoveTo = new Vector3(apexPos.x, racketY, apexPos.z);
		float durationTillHit = (totalDuration - lastStage.duration + lastStage.flight.apexTime) / ball.flightPlan.totalTimeScale - Time.deltaTime;
		float moveSpeed = GetNewMoveSpeed();
		float moveDuration = Vector3Ex.HorizontalDistance(positionToMoveTo, racket.position) / moveSpeed;
		if (moveDuration <= durationTillHit)
		{
			AIHitBallParams hitParams = GetHitBallParams(positionToMoveTo);
			MoveTo(positionToMoveTo, durationTillHit, delegate
			{
				HitBall(hitParams.hitBallParams);
			});
			Vector3 normalized = (positionToMoveTo - hitParams.hitBallParams.landingPosition).OnGround().normalized;
			float num = 0.2f;
			float num2 = num * hitParams.usedSpeed;
			normalized *= num2;
			StartCoroutine(DoAdditionalMove(Vector3.zero, normalized, 0f, durationTillHit - num, num, null));
		}
	}
}
